/*******************************************************************************
 * Copyright (c) 2016 Hangzhou C-SKY Microsystems Co., Ltd.All rights reserved.
 *******************************************************************************/
#include "flash_ins.h"
#define REG32(addr,offset) *((volatile unsigned int *)(addr+(offset<<2)))
#define REG32_1BYTE(addr,offset) *((volatile unsigned int *)(addr+offset))

#define CK_UINT8  unsigned char
#define CK_UINT16 unsigned short
#define CK_UINT32 unsigned int

#define SUCCESS   0
#define WARNNING_addressMisaligned   1
#define WARNNING_lengthMisaligned    2
#define ERROR_exceed               -1
#define ERROR_operationFailed      -2

#define POLLING_BIT_DQ7_HALF   0x80
#define POLLING_BIT_DQ7   0x00800080

#define dbg_printf(x)	do{}while(0)

#define FLASH_START 0
#define FLASH_END   0x800000

#define FLASH_SECTOR_SIZE   0x2000   // 8kb
#define FLASH_BLOCK_SIZE    0x20000  // 128Kb
#define ERASE_BLOCK_CMD     0x00300030
#define ERASE_SECTOR_CMD    0x00500050

#define SECTOR_SIZE			0x2
#define NUM_SECTORS			0x10
#define NUM_PAGES			0x10000
#define PAGE_SIZE			16

void FlashRead(CK_UINT32 Address, CK_UINT32 ByteCount, CK_UINT32 Command);
void FlashWrite(CK_UINT32 Address, CK_UINT32 ByteCount, CK_UINT32 Command);
void FlashErase(CK_UINT32 Address, CK_UINT32 ByteCount, CK_UINT32 Command);


//FLASH BUFFER中各数据的偏移量
#define COMMAND_OFFSET		0 // FLASH instruction
#define ADDRESS_1_OFFSET	1 // MSB byte of address to read or write
#define ADDRESS_2_OFFSET	2 // Middle byte of address to read or write
#define ADDRESS_3_OFFSET	3 // LSB byte of address to read or write
#define DATA_OFFSET			4 // Start of Data for Read/Write
#define DUMMY_OFFSET		4 // Dummy byte offset for reads

/**
 * Driver for flash program.
 */

/**
 * ERROR TYPE. MUST NOT BE MODIFIED
 */
#define ERROR_INIT      -200
#define ERROR_READID    -201
#define ERROR_PROGRAM   -202
#define ERROR_READ      -203
#define ERROR_ERASE     -204
#define ERROR_CHIPERASE -205

/**
 * Customize this method to perform any initialization
 * needed to access your flash device.
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_INIT,
 * Otherwise return 0.
 */
int  flashInit(){
	// TODO 
	CK_UINT8 flashID;
      //读取ID系数
	REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) = INST_READID;
    flashID = REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) ;
    printf("flashID: %d\n",flashID);

	//配置SPI时钟
	REG32(CTRL_REGS_ADDR_START, CONFIG_REG_OFFSET) = CONFIG_CLK_SET;
	REG32(CTRL_REGS_ADDR_START, SPICLK_DIV_REG_OFFSET) = CLK_PRESCALE_2;
    printf("SPI CLK DIV: %d\n",REG32(CTRL_REGS_ADDR_START, SPICLK_DIV_REG_OFFSET));
    //配置SPI CLK MODE
	REG32(CTRL_REGS_ADDR_START, CONFIG_REG_OFFSET) = CONFIG_CLK_MODE_SET;
	REG32(CTRL_REGS_ADDR_START, SPICLK_MODE_REG_OFFSET) = CLK_MODE_0;
    printf("SPI CLK DIV: %d\n",REG32(CTRL_REGS_ADDR_START, SPICLK_MODE_REG_OFFSET));
	REG32(CTRL_REGS_ADDR_START, SPICLK_MODE_REG_OFFSET) = CLK_MODE_1;
    printf("SPI CLK DIV: %d\n",REG32(CTRL_REGS_ADDR_START, SPICLK_MODE_REG_OFFSET));
  return SUCCESS;
}

/**
 * Customize this method to read flash ID
 *
 * @param flashID: returns for flash ID
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_READID,
 * Otherwise return 0.
 */
int  flashID(unsigned int* flashID){
	CK_UINT16 mid = 0;
	CK_UINT16 did = 0;

    *(volatile CK_UINT32 *)(CTRL_REGS_ADDR_START+COMD_REG_OFFSET) = INST_READID;
    *flashID = *(volatile CK_UINT32 *)(CTRL_REGS_ADDR_START+DEVICE_ID_REG_OFFSET) ;
    
	// CK_NORFlash_ReadID(&mid, &did);
	// *flashID = (mid << 16) | did;
	return SUCCESS;
}
#define PAGE_SIZE 16
/* Number of flash pages to be written.*/
#define PAGE_COUNT 2

#define TEST_ADDR 0x3000

#define MAX_DATA		(PAGE_COUNT * PAGE_SIZE)
CK_UINT8 WriteBuffer[PAGE_SIZE + DATA_OFFSET];

void flashTest()
{
	int Status;
	CK_UINT8 *BufferPtr;
	CK_UINT8 UniqueValue;
	
	int Count;
	int Page;
	// spiflash_Config *flashConfig;
	//spiflashPs *flashInstancePtr;
	//初始化，读取ID，设置分频系数
	//flashConfig = flashInit(flashInstancePtr);
	//初始化读写BUFFER
	for (UniqueValue = 0, Count = 0; Count < PAGE_SIZE;
	     Count++, UniqueValue++) {
		WriteBuffer[Count] = (CK_UINT8)(UniqueValue + 5);
	}
	//初始化flash，读取ID，配置SPI时钟及模式
    flashInit();

    //擦除Flash
	FlashErase(TEST_ADDR, MAX_DATA, INST_BE32);


	//向FLASH中写入数据
	for (Page = 0; Page < PAGE_COUNT; Page++) {
		FlashWrite( TEST_ADDR, PAGE_SIZE, INST_WRITE1B);
	}
	//从FLASH中读出数据
	FlashRead(TEST_ADDR, MAX_DATA, INST_READ);

}



/*****************************************************************************/
/**
*
* This function writes to the  serial FLASH connected to the QSPI interface.
* All the data put into the buffer must be in the same page of the device with
* page boundaries being on 256 byte boundaries.
*
* @param	QspiPtr is a pointer to the QSPI driver component to use.
* @param	Address contains the address to write data to in the FLASH.
* @param	ByteCount contains the number of bytes to write.
* @param	Command is the command used to write data to the flash. QSPI
*		device supports only Page Program command to write data to the
*		flash.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void FlashWrite(CK_UINT32 Address, CK_UINT32 ByteCount, CK_UINT32 Command)
{
	//u8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	//u8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	CK_UINT8 FlashStatus;
	CK_UINT8 Count;
	CK_UINT8 data_buff;
    for ( Count = 0; Count < PAGE_SIZE; Count++) {
		data_buff = WriteBuffer[Count] ;
		REG32_1BYTE(TX_BUF_ADDR_START, Count) = data_buff;
		//printf("data:%d\n",Count);
	}

	/*
	 * Send the write enable command to the FLASH so that it can be
	 * written to, this needs to be sent as a seperate transfer before
	 * the write
	 */
	// XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
	// 			sizeof(WriteEnableCmd));


	/*
	 * Setup the write command with the specified address and data for the
	 * FLASH
	 */
	// WriteBuffer[COMMAND_OFFSET]   = Command;
	// WriteBuffer[ADDRESS_1_OFFSET] = (u8)((Address & 0xFF0000) >> 16);
	// WriteBuffer[ADDRESS_2_OFFSET] = (u8)((Address & 0xFF00) >> 8);
	// WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);

	/*
	 * Send the write command, address, and data to the FLASH to be
	 * written, no receive buffer is specified since there is nothing to
	 * receive
	 */
	// XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
	// 			ByteCount + OVERHEAD_SIZE);
		REG32(CTRL_REGS_ADDR_START, ADDR_REG_OFFSET) = Address;
		REG32(CTRL_REGS_ADDR_START, DATA_LEN_REG_OFFSET) = ByteCount;
		REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) = Command;
		REG32(CTRL_REGS_ADDR_START, START_REG_OFFSET) = FLASH_START_WORK;
	/*
	 * Wait for the write command to the FLASH to be completed, it takes
	 * some time for the data to be written
	 */
	while (1) {
		/*
		 * Poll the status register of the FLASH to determine when it
		 * completes, by sending a read status command and receiving the
		 * status byte
		 */
		// XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd, FlashStatus,
		// 			sizeof(ReadStatusCmd));

		/*
		 * If the status indicates the write is done, then stop waiting,
		 * if a value of 0xFF in the status byte is read from the
		 * device and this loop never exits, the device slave select is
		 * possibly incorrect such that the device status is not being
		 * read
		 */
			FlashStatus = REG32(CTRL_REGS_ADDR_START, STATUS_REG_OFFSET);
			printf("write status is %d\n",FlashStatus);
			if ((FlashStatus & 0x01) == 1) {
				break;
			}else if((FlashStatus & 0x04) == 1)
			{
				printf("write error :%d",FlashStatus);
				break;
			}
	}
}
/*****************************************************************************/
/**
*
* This function reads from the  serial FLASH connected to the
* QSPI interface.
*
* @param	QspiPtr is a pointer to the QSPI driver component to use.
* @param	Address contains the address to read data from in the FLASH.
* @param	ByteCount contains the number of bytes to read.
* @param	Command is the command used to read data from the flash. QSPI
*		device supports one of the Read, Fast Read, Dual Read and Fast
*		Read commands to read data from the flash.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void FlashRead(CK_UINT32 Address, CK_UINT32 ByteCount, CK_UINT32 Command)
{
	/*
	 * Setup the write command with the specified address and data for the
	 * FLASH
	 */
	CK_UINT8 FlashStatus;

	REG32(CTRL_REGS_ADDR_START, ADDR_REG_OFFSET) = Address;
	REG32(CTRL_REGS_ADDR_START, DATA_LEN_REG_OFFSET) = ByteCount;
	REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) = Command;
	REG32(CTRL_REGS_ADDR_START, START_REG_OFFSET) = FLASH_START_WORK;
	while (1)
	{
         FlashStatus = REG32(CTRL_REGS_ADDR_START, STATUS_REG_OFFSET);
		printf("read status is %d\n",FlashStatus);
		if ((FlashStatus & 0x01) == 1) {
			break;
		}else if((FlashStatus & 0x04) == 1)
		{
			printf("read error :%d",FlashStatus);
			break;
		}
	}
	


	// if ((Command == FAST_READ_CMD) || (Command == DUAL_READ_CMD) ||
	//     (Command == QUAD_READ_CMD)) {
	// 	ByteCount += DUMMY_SIZE;
	// }
	/*
	 * Send the read command to the FLASH to read the specified number
	 * of bytes from the FLASH, send the read command and address and
	 * receive the specified number of bytes of data in the data buffer
	 */
	// XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, ReadBuffer,
	// 			ByteCount + OVERHEAD_SIZE);
}
/*****************************************************************************/
/**
*
* This function erases the sectors in the  serial FLASH connected to the
* QSPI interface.
*
* @param	QspiPtr is a pointer to the QSPI driver component to use.
* @param	Address contains the address of the first sector which needs to
*		be erased.
* @param	ByteCount contains the total size to be erased.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void FlashErase(CK_UINT32 Address, CK_UINT32 ByteCount,CK_UINT32 Command)
{
	//CK_UINT8 WriteEnableCmd = { WRITE_ENABLE_CMD };
	//CK_UINT8 ReadStatusCmd[] = { READ_STATUS_CMD, 0 };  /* must send 2 bytes */
	//CK_UINT8 ReadStatusCmd[] = { INST_READ};
	CK_UINT8 FlashStatus;
	int Sector;

	/*
	 * If erase size is same as the total size of the flash, use bulk erase
	 * command 
	 */
	if (ByteCount == (NUM_SECTORS * SECTOR_SIZE)) {
		/*
		 * Send the write enable command to the FLASH so that it can be
		 * written to, this needs to be sent as a seperate transfer
		 * before the erase  硬件中写和擦除操作已经在FSM中实现了写使能
		 */
		// XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
		// 		  sizeof(WriteEnableCmd));

		/* Setup the bulk erase command*/
		// WriteBuffer[COMMAND_OFFSET]   = BULK_ERASE_CMD;

        //flashID = REG32(CTRL_REGS_ADDR_START+COMD_REG_OFFSET) ;

		/*
		 * Send the bulk erase command; no receive buffer is specified
		 * since there is nothing to receive
		 */
		// XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
		// 			BULK_ERASE_SIZE);
		REG32(CTRL_REGS_ADDR_START, ADDR_REG_OFFSET) = Address;
		REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) = Command;
		REG32(CTRL_REGS_ADDR_START, START_REG_OFFSET) = FLASH_START_WORK;
		/* Wait for the erase command to the FLASH to be completed*/
		while (1) {
			/*
			 * Poll the status register of the device to determine
			 * when it completes, by sending a read status command
			 * and receiving the status byte
			 */
			
			// XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
			// 			FlashStatus,
			// 			sizeof(ReadStatusCmd));

			/*
			 * If the status indicates the write is done, then stop
			 * waiting; if a value of 0xFF in the status byte is
			 * read from the device and this loop never exits, the
			 * device slave select is possibly incorrect such that
			 * the device status is not being read
			 */
			FlashStatus = REG32(CTRL_REGS_ADDR_START, STATUS_REG_OFFSET);
			printf("block erease status is %d\n",FlashStatus);
			if ((FlashStatus & 0x01) == 1) {
				break;
			}
		}

		return;
	}

	/*
	 * If the erase size is less than the total size of the flash, use
	 * sector erase command
	 */
	for (Sector = 0; Sector < ((ByteCount / SECTOR_SIZE) + 1); Sector++) {
		/*
		 * Send the write enable command to the SEEPOM so that it can be
		 * written to, this needs to be sent as a seperate transfer
		 * before the write
		 */
		// XQspiPs_PolledTransfer(QspiPtr, &WriteEnableCmd, NULL,
		// 			sizeof(WriteEnableCmd));

		/*
		 * Setup the write command with the specified address and data
		 * for the FLASH 24bit 先输出高位、再输出中位，最后输出低位
		 */
		// WriteBuffer[COMMAND_OFFSET]   = SEC_ERASE_CMD;
		// WriteBuffer[ADDRESS_1_OFFSET] = (u8)(Address >> 16);
		// WriteBuffer[ADDRESS_2_OFFSET] = (u8)(Address >> 8);
		// WriteBuffer[ADDRESS_3_OFFSET] = (u8)(Address & 0xFF);

		// /*
		//  * Send the sector erase command and address; no receive buffer
		//  * is specified since there is nothing to receive
		//  */
		// XQspiPs_PolledTransfer(QspiPtr, WriteBuffer, NULL,
		// 			SEC_ERASE_SIZE);

		REG32(CTRL_REGS_ADDR_START, ADDR_REG_OFFSET) = Address;
		REG32(CTRL_REGS_ADDR_START, COMD_REG_OFFSET) = INST_SE;
		REG32(CTRL_REGS_ADDR_START, START_REG_OFFSET) = FLASH_START_WORK;
		/*
		 * Wait for the sector erse command to the
		 * FLASH to be completed
		 */
		while (1) {
			/*
			 * Poll the status register of the device to determine
			 * when it completes, by sending a read status command
			 * and receiving the status byte
			 */
			// XQspiPs_PolledTransfer(QspiPtr, ReadStatusCmd,
			// 			FlashStatus,
			// 			sizeof(ReadStatusCmd));

			/*
			 * If the status indicates the write is done, then stop
			 * waiting, if a value of 0xFF in the status byte is
			 * read from the device and this loop never exits, the
			 * device slave select is possibly incorrect such that
			 * the device status is not being read
			 */
			FlashStatus = REG32(CTRL_REGS_ADDR_START, STATUS_REG_OFFSET);
			printf("sector erease status is %d\n",FlashStatus);
			
			if ((FlashStatus & 0x01) == 1) {
				break;
			}
		}

		Address += SECTOR_SIZE;
	}
}